package com.kongjs.application.service.impl;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kongjs.application.cache.UserCacheManagerImpl;
import com.kongjs.application.convert.PermissionsConverter;
import com.kongjs.application.exception.ApiParamCheckException;
import com.kongjs.application.model.dto.*;
import com.kongjs.application.model.entity.Permissions;
import com.kongjs.application.mapper.PermissionsMapper;
import com.kongjs.application.model.entity.RolePermission;
import com.kongjs.application.service.IPermissionsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kongjs.application.service.IRolePermissionService;
import com.kongjs.application.tenant.TenantRoleHierarchyMessage;
import com.kongjs.application.util.PageUtil;
import com.kongjs.common.mybatis.tenant.TenantContextHolder;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Transactional
@Service
public class PermissionsServiceImpl extends ServiceImpl<PermissionsMapper, Permissions> implements IPermissionsService {
    @Resource
    private TenantRoleHierarchyMessage tenantRoleHierarchyMessage;
    @Resource
    private PermissionsConverter permissionsConverter;
    @Resource
    private IRolePermissionService rolePermissionService;

    @Override
    public boolean save(SavePermissionDTO dto) {
        Permissions permissions = permissionsConverter.toEntity(dto);
        save(permissions);
        PermissionCacheDTO cache = permissionsConverter.toCache(permissions);
        UserCacheManagerImpl.getInstance().savePermission(cache);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public boolean remove(Long id) {
        rolePermissionService.lambdaUpdate().eq(RolePermission::getPermissionId, id).remove();
        removeById(id);
        UserCacheManagerImpl.getInstance().removePermissionCache(id);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public boolean update(UpdatePermissionDTO dto) {
        if (!ObjectUtils.isEmpty(dto.getParentId())) {
            if (dto.getId().equals(dto.getParentId())){
                throw new ApiParamCheckException("设置的上级权限不能与当前权限相同");
            }
            List<Permissions> permissions = lambdaQuery().eq(Permissions::getEnabled, 1).list();
            List<TreeNode<Long>> treeNodes = new ArrayList<>();
            for (Permissions permission : permissions) {
                TreeNode<Long> node = new TreeNode<>();
                node.setId(permission.getId());
                node.setParentId(dto.getParentId());
                treeNodes.add(node);
            }
            List<Tree<Long>> trees = TreeUtil.build(treeNodes, null);
            for (Tree<Long> tree : trees) {
                Tree<Long> parentNode = TreeUtil.getNode(tree, dto.getParentId());
                if (ObjectUtils.isEmpty(parentNode)){
                    continue;
                }
                List<Long> parentsId = TreeUtil.getParentsId(parentNode, false);
                // 要设置父级节点存在当前节点作为某个上级,循环引用
                if (parentsId.contains(dto.getId())) {
                    throw new ApiParamCheckException("设置的上级权限不能是当前权限的子级");
                }
            }
        }
        Permissions permissions = permissionsConverter.toEntity(dto);
        updateById(permissions);
        PermissionCacheDTO cache = permissionsConverter.toCache(permissions);
        UserCacheManagerImpl.getInstance().savePermission(cache);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public QueryPermissionDTO query(Long id) {
        Permissions permissions = getById(id);
        return permissionsConverter.toDto(permissions);
    }

    @Override
    public List<QueryPermissionDTO> queryList() {
        List<Permissions> permissions = list();
        return permissionsConverter.toDtos(permissions);
    }

    @Override
    public List<QueryPermissionDTO> trees() {
        List<Permissions> permissions = lambdaQuery().eq(Permissions::getEnabled,1).list();
        return permissionsConverter.toDtos(permissions);
    }

    @Override
    public PageDTO<QueryPermissionDTO> page(PageQueryDTO dto, QueryPermissionParamDTO paramDTO) {
        Page<Permissions> page;
        if (!ObjectUtils.isEmpty(paramDTO)) {
            page = lambdaQuery()
                    .eq(!ObjectUtils.isEmpty(paramDTO.getId()), Permissions::getId, paramDTO.getId())
                    .like(StringUtils.hasText(paramDTO.getPermissionName()), Permissions::getPermissionName, paramDTO.getPermissionName())
                    .like(StringUtils.hasText(paramDTO.getAuthority()), Permissions::getAuthority, paramDTO.getAuthority())
                    .like(StringUtils.hasText(paramDTO.getRemark()),Permissions::getRemark,paramDTO.getRemark())
                    .eq(!ObjectUtils.isEmpty(paramDTO.getEnabled()),Permissions::getEnabled,paramDTO.getEnabled())
                    .between(!ObjectUtils.isEmpty(paramDTO.getStartDate()) && !ObjectUtils.isEmpty(paramDTO.getEndDate()), Permissions::getCreateDate, paramDTO.getStartDate(), paramDTO.getEndDate())
                    .orderByDesc(Permissions::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        } else {
            page = lambdaQuery()
                    .orderByDesc(Permissions::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        }
        IPage<QueryPermissionDTO> iPage = page.convert(permissions -> permissionsConverter.toDto(permissions));
        return PageUtil.pageDto(iPage);
    }
}
